/*
 * MutableTree.scala
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package trees.tree

import trees.settings.Settings


class MutableTree (override val name:String, var rooted:Boolean,
                   override val weight:Double, var accessNode:MutableNode ,
                   var treeList:List[Int], var idList:List[Int])extends AbstractTree(name, rooted, weight, accessNode,treeList,idList) {

    def getLeafs():List[Int] = {
        accessNode.getLeafs()
        //accessNode.children.foldLeft(List[Int]()){ (childs:List[Int], n) => childs ::: n.getLeafs() }
    }
    
    def changeMutability():ImmutableTree = {
        accessNode.changeMutability match {
            case im:ImmutableNode =>
                new ImmutableTree(name,rooted,weight,im,treeList,idList)
            case _ =>
                throw new Error("Error converting the accessNode to a mutable node")
        }
        
    }

    def updateNodes():Unit = {
        //updates children nodes and [Int]-representations
        def update(node:AbstractLeaf,treeStart:Int,idStart:Int):(List[Int],List[Int]) = node match {
            case l:Leaf =>
                (List[Int](l.id),List[Int](l.id))
            case n:MutableNode =>
                n.treeStart=treeStart
                n.idStart=idStart
                var result:(List[Int],List[Int])= (List[Int](IdFactory.getID("(")),List[Int]())
                for(child <-n.children){
                    val tmp=update(child,result._1.size,result._2.size)
                    result = (result._1 :::tmp._1,result._2:::tmp._2)
                }
                n.treeEnd=treeStart+result._1.size+1
                n.idEnd=idStart+result._2.size
                (result._1 ::: List[Int](IdFactory.getID(")")),result._2)
        }

        val result=update(accessNode,0,0)
        treeList=result._1
        idList=result._2
    }

    def reRoot(ids:List[Int]):Unit ={
        //reroot has to be done twice to ensure that the minimum common node is found
        if(ids.size==ids.intersect(idList).size){
            //reroot
            //updateNodes()???
            def shift(node:MutableNode):MutableNode ={
//                println(node)
                var i= -1
                var continue=false
                while(!continue&&i<node.children.size-1){
                    i+=1
                    node.children(i) match {
                        case l:Leaf  if (ids.size==1)=>
//                            continue=l.id==ids(0)
                            continue=false
                        case n:MutableNode =>
                            continue= ids.size==ids.intersect(idList.slice(n.idStart,n.idEnd)).size
                    }
                }
                if(continue){
                    //do a shift towards the the selected node
                    val newNode= new MutableNode(List[AbstractLeaf](),0,0,0,0)
                    node.children(i) match {
                        case l:Leaf =>
                            newNode.addChild(l)
                        case n:MutableNode =>
                            newNode.addChild(n.children)
                    }
                    val toAdd=node.children.slice(0,i):::node.children.slice(i+1,node.children.size)
                    toAdd(0) match {
                        case l:Leaf if(toAdd.size==1) =>
                            newNode.addChild(toAdd(0))
                        case _ =>
                            newNode.addChild(new MutableNode(toAdd,0,0,0,0))
                    }
                    shift(newNode)
                }else{
                    node.addChild(new Leaf(IdFactory.getID("root")))
                    node
                }
            }
            if(rooted){
                //remove the root-node and reroot
                accessNode= shift(new MutableNode(accessNode.children drop 1, accessNode.treeStart,accessNode.treeEnd,accessNode.idStart,accessNode.idEnd))
            }else{
                accessNode=shift(accessNode)
            }
            rooted=true
            updateNodes
        }else{
            throw new Error("There exists ids in the list that are not included in the tree: "+ids.filter((e1)=>idList.exists(e1!=_)).foldLeft("")(_+"\n"+_))
            ()
        }
    }

    def reRoot(id:Int):Unit = {
        reRoot(List[Int](id))
    }

    //    def reRoot(label:String):Unit = {
    //        reRoot(List[String](label))
    //    }
    //
    //    def reRoot(labels:List[String]):Unit = {
    //        if(labels.map(IdFactory.contains(_)).foldLeft(true)(_&&_)){
    //            reRoot(labels.map(IdFactory.getID(_)))
    //        }else{
    //            throw new Error("No tree contains the the labels: "+labels.filter(IdFactory.contains(_)).foldLeft("")(_+"\n"+_))
    //        }
    //    }
    
  override def hashCode():Int = {
      treeList.hashCode
  }

  override def equals(t:Any):Boolean = t match{
      case t:ImmutableTree =>
          treeList == t.treeList
            //name == t.name && weight == t.weight && rooted == t.rooted && accessNode.equals(t.accessNode)
      case t:MutableTree =>
          treeList == t.treeList
        //name == t.name && weight == t.weight && rooted == t.rooted && accessNode.equals(t.accessNode)
      case t:AbstractTree =>
        name == t.name && weight == t.weight
      case _ => false
  }



}


